
/*******************************************************************************
**                      Includes                                              **
*******************************************************************************/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "random.h"
#include "big.h"
#include "sm2.h"
#include "sm3.h"
/*******************************************************************************
**                      Static Macro Definitions                             **
*******************************************************************************/
// SM2 a
static const unsigned char SM2_ECC_A[] = {
        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC
};

// SM2 b
static const unsigned char SM2_ECC_B[] = {
        0x28, 0xE9, 0xFA, 0x9E, 0x9D, 0x9F, 0x5E, 0x34,
        0x4D, 0x5A, 0x9E, 0x4B, 0xCF, 0x65, 0x09, 0xA7,
        0xF3, 0x97, 0x89, 0xF5, 0x15, 0xAB, 0x8F, 0x92,
        0xDD, 0xBC, 0xBD, 0x41, 0x4D, 0x94, 0x0E, 0x93
};

// SM2 Gx
static const unsigned char SM2_ECC_GX[] = {
        0x32, 0xC4, 0xAE, 0x2C, 0x1F, 0x19, 0x81, 0x19,
        0x5F, 0x99, 0x04, 0x46, 0x6A, 0x39, 0xC9, 0x94,
        0x8F, 0xE3, 0x0B, 0xBF, 0xF2, 0x66, 0x0B, 0xE1,
        0x71, 0x5A, 0x45, 0x89, 0x33, 0x4C, 0x74, 0xC7
};

// SM2 Gy
static const unsigned char SM2_ECC_GY[] = {
        0xBC, 0x37, 0x36, 0xA2, 0xF4, 0xF6, 0x77, 0x9C,
        0x59, 0xBD, 0xCE, 0xE3, 0x6B, 0x69, 0x21, 0x53,
        0xD0, 0xA9, 0x87, 0x7C, 0xC6, 0x2A, 0x47, 0x40,
        0x02, 0xDF, 0x32, 0xE5, 0x21, 0x39, 0xF0, 0xA0
};

// SM2 N-1
static const sm2_bn_t SM2_BN_N_SUB_ONE = {
        0x39D54122, 0x53BBF409, 0x21C6052B, 0x7203DF6B,
        0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE
};

static const sm2_bn_t SM2_BN_2W = {
    0x00000000, 0x00000000, 0x00000000, 0x80000000,
    0x00000000, 0x00000000, 0x00000000, 0x00000000
};

static const sm2_bn_t SM2_BN_2W_SUB_ONE = {
    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x7FFFFFFF,
    0x00000000, 0x00000000, 0x00000000, 0x00000000
};

/*******************************************************************************
**                      Global Macro Definitions                             **
*******************************************************************************/
uint8 PublicKey[] = 	{0x50, 0xE2, 0x78, 0xAB, 0xBA, 0xB4, 0x5C, 0xA2, 0xE8, 0x13, 0x41, 0x99, 0xE0, 0xED, 0xE0, 0xCA,
                        0xA6, 0x94, 0xD3, 0xDE, 0x26, 0xC9, 0xFC, 0xEF, 0xFC, 0xDF, 0xEE, 0xDC, 0x85, 0x92, 0xCC, 0x9D,
                        0x4E, 0x6E, 0xDE, 0x36, 0x2A, 0x2C, 0x62, 0x47, 0xA1, 0x56, 0x33, 0x63, 0x84, 0xDC, 0x9C, 0x09,
                        0xE3, 0x6B, 0x14, 0xB5, 0x63, 0x9B, 0xEB, 0xDD, 0x1B, 0x99, 0xC9, 0x2A, 0x4E, 0x46, 0x44, 0xCD,};
uint8 PrivateKey[] = {0xBF, 0x72, 0x24, 0x72, 0x6B, 0xCF, 0x10, 0x2D, 0xF8, 0x55, 0x91, 0x52, 0x46, 0xB1, 0x13, 0x65,
                        0x5B, 0x39, 0x76, 0xF4, 0xD2, 0xA4, 0xD8, 0x49, 0x7A, 0x14, 0xAD, 0xD5, 0x0D, 0x82, 0xF2, 0x9C,};;

/*******************************************************************************
**                      Private Function Declarations                         **
*******************************************************************************/
static void Cry_SM2GenZA(uint8 *id, uint32 idlen, uint8 *hash)
{
	uint8 idlen_char[2];
	sm3_ctx md[1];

	put_unaligned_be16(idlen<<3, idlen_char);

	Cry_SM3Init(md);
	Cry_SM3Update(md, idlen_char, 2);
	Cry_SM3Update(md, id, idlen);
	Cry_SM3Update(md, SM2_ECC_A, ECC_NUMWORD);
	Cry_SM3Update(md, SM2_ECC_B, ECC_NUMWORD);
	Cry_SM3Update(md, SM2_ECC_GX, ECC_NUMWORD);
	Cry_SM3Update(md, SM2_ECC_GY, ECC_NUMWORD);
	Cry_SM3Update(md, PublicKey, ECC_NUMWORD);
	Cry_SM3Update(md, &PublicKey[32], ECC_NUMWORD);
	Cry_SM3Final(md, hash);

}

/*******************************************************************************
**                      Global Function Definitions                           **
*******************************************************************************/

Std_ReturnType Cry_SM2Sign(uint8 *Message, uint32 MessageLen, uint8 *User_Id, uint32 User_Len, uint8 *SignatureR, uint8 *SignatureS)
{
	Std_ReturnType Result = E_OK;
	uint8 zhash[ECC_NUMWORD];
	uint8 mhash[ECC_NUMWORD];
	uint8 mMessage[ECC_NUMBITS * 8];

	sm2_point_t _P, *P = &_P;
    sm2_bn_t d;
    sm2_bn_t e;
    sm2_bn_t k;
    sm2_bn_t x;
    sm2_bn_t r;
    sm2_bn_t s;
	uint8 buf[256];
	sm2_bn_t key;
	sm2_bn_t dgst;

	/* Generate pre-compiled data Za */
	Cry_SM2GenZA(User_Id,User_Len,zhash);
	/* Generate pre-compiled data mMessage */
	memcpy(mMessage,zhash,ECC_NUMWORD);
	memcpy(&mMessage[ECC_NUMWORD],Message,MessageLen);

	/* Generate the Hash Value of pre-compiled data mMessage */
	Cry_SM3MainFunction(mMessage, (MessageLen + ECC_NUMWORD), mhash);

	/* Get the bignum of mhash */
	Cry_SM2BnFromBytes(dgst, mhash);

	/* get the big num of Prikey */
	Cry_SM2FormHex(key, PrivateKey);

	/* generate point of prikey */
    Cry_SM2BnToMont(d, key, GM_BN_N);

    // e = H(M)
    gm_bn_copy(e, mhash);

    Cry_SM2RandomGenInit();

retry:
        do {
            do {
                Cry_SM2RandomGenMainFunction(buf, 256);
                Cry_SM2BnFromBytes(k, buf);
            } while (Cry_SM2Cmp(k, GM_BN_N) >= 0);
        } while (Cry_SM2BnIsZero(k));

    // (x, y) = kG
    Cry_SM2PointMul(P, k, GM_MONT_G);
    Cry_SM2PointGetXY(P, x, NULL);


    // r = e + x (mod n)
    Cry_SM2Add(r, e, x, GM_BN_N);

    /* if r == 0 or r + k == n re-generate k */
    if (Cry_SM2BnIsZero(r)) {
        goto retry;
    }
    Cry_SM2Add(x, r, k, GM_BN_N);
    if (Cry_SM2BnIsZero(x)) {
        goto retry;
    }

    Cry_SM2BnToBytes(r, SignatureR);

    /* s = ((1 + d)^-1 * (k - r * d)) mod n */
    Cry_SM2BnToMont(r, r, GM_BN_N);
    Cry_SM2BnToMont(k, k, GM_BN_N);

    Cry_SM2BnMontMul(e, r, d, GM_BN_N);
    Cry_SM2BnSub(k, k, e, GM_BN_N);

    Cry_SM2Add(x, GM_BN_MONT_NONE, d, GM_BN_N);
    Cry_SM2BnInv(x, x, GM_BN_N);
    Cry_SM2BnMontMul(s, x, k, GM_BN_N);

    if(Cry_SM2BnIsZero(s)) {
        goto retry;
    }

    Cry_SM2BnFromMont(s, s, GM_BN_N);
    Cry_SM2BnToBytes(s, SignatureS);

	return Result;
}

Std_ReturnType Cry_SM2Verify(uint8 *Message, uint32 MessageLen, uint8 *User_Id, uint32 User_Len, uint8 *SignatureR, uint8 *SignatureS)
{
	Std_ReturnType Result = E_OK;
	uint8 zhash[ECC_NUMWORD];
	uint8 mhash[ECC_NUMWORD];
	uint8 mMessage[ECC_NUMBITS * 4];
	sm3_ctx mza[1];

	sm2_point_t zPubkey;
	sm2_point_t _P, *P = &_P;
    sm2_point_t _Q, *Q = &_Q;
    sm2_bn_t r;
    sm2_bn_t s;
    sm2_bn_t e;
    sm2_bn_t x;
    sm2_bn_t t;
	

	/* Generate pre-compiled data Za */
	Cry_SM2GenZA(User_Id,User_Len,zhash);
	/* Generate pre-compiled data mMessage */
	memcpy(mMessage,zhash,ECC_NUMWORD);
	memcpy(&mMessage[ECC_NUMWORD],Message,MessageLen);

	/* Generate the Hash Value of pre-compiled data mMessage */
	Cry_SM3MainFunction(mMessage, (MessageLen + ECC_NUMWORD), mhash);

	/* get the big num of Prikey */
	Cry_SM2PointFromHex(&zPubkey, PublicKey);

	// parse signature values
    Cry_SM2BnFromBytes(r, (const uint8 *)SignatureR);
    Cry_SM2BnFromBytes(s, (const uint8 *)SignatureS);
    if (Cry_SM2BnIsZero(r)
        || Cry_SM2Cmp(r, GM_BN_N) >= 0
        || Cry_SM2BnIsZero(s)
        || Cry_SM2Cmp(s, GM_BN_N) >= 0) {
        return -1;
    }

    // parse public key
    gm_point_copy(P, &zPubkey);

    // t = r + s (mod n)
    // check t != 0
    Cry_SM2Add(t, r, s, GM_BN_N);
    if (Cry_SM2BnIsZero(t)) {
        return -1;
    }

    // Q = s * G + t * P
    Cry_SM2PointMul(Q, s, GM_MONT_G);
    Cry_SM2PointMul(P, t, P);
    Cry_SM2PointAdd(Q, Q, P);
    Cry_SM2PointGetXY(Q, x, NULL);

    // e  = H(M)
    // r' = e + x (mod n)
    gm_bn_copy(e, mhash);
    Cry_SM2Add(e, e, x, GM_BN_N);

    // check if r == r'
    if (Cry_SM2Cmp(e, r) == 0) {
        return 1;
    } else {
        return 0;
    }
	return Result;
}
